package cn.xenosp.server.common.logs;

import java.io.*;
import java.util.*;

public class Logs {
	//日志文件最大尺寸（单位：M）
	public static Long fileMaxSize=new Long(10);

	//工程名称
	private String projectName=null;
	
	//最大文件个数
	private Integer maxFileNumber=5;

	//控制台打印日志。1：输出；0：不输出
	private Integer isConsoleOut=1;

	//文件日志。1：输出；0：不输出
	private Integer isFileOut=1;
	
	//超时告警值（单位秒）。
	private Integer timeout_alarm=60;

	//要输出的类名
	private String className="";
	
	//日志文件输出路径
	private String logFilePath="";
	private String logFileName="";
	
	private String infoFileFullName="";
	private PrintWriter infoFileOutLog=null;
	
	private String errorFileFullName="";
	private PrintWriter errorFileOutLog=null;
	
	private String warnFileFullName="";
	private PrintWriter warnFileOutLog=null;
	
	private String timeoverFileFullName="";
	private PrintWriter timeoverFileOutLog=null;
	
	
	private static Logs instanceFile = null;
	private Properties prop = new Properties();
	
	public static Map<String,Logs> instanceFiles = new HashMap<String,Logs>();
	
	long startTime=0;
	long endTime=0;
	/////////////////////////////////////////////////////////////////////////////////////////
	static {
		new Thread(){
			@Override
			public void run() {
				try {
					while(true){
						for(Map.Entry<String, Logs> entry : Logs.instanceFiles.entrySet()){
							Logs logs = entry.getValue();
							if(logs.getInfoLength()>Logs.fileMaxSize*1024*1024){
								logs.newInfoFile();
							}

							if(logs.getWarnLength()>Logs.fileMaxSize*1024*1024){
								logs.newWarnFile();
							}

							if(logs.getErrorLength()>Logs.fileMaxSize*10024*1024){
								logs.newErrorFile();
							}
						}

						//5分钟
						Thread.sleep(1000*60*5);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}.start();
	}
	
	/////////////////////////////////////////////////////////////////////////////////////////
	public static synchronized Logs getInstance() {
		return getInstance("wsLog",null);
	}
	
	public static synchronized  Logs getInstance(String logFileName) {
		return getInstance(logFileName,null);
	}

	public static synchronized  Logs getInstance(String logFileName,@SuppressWarnings("rawtypes") Class clazz) {
		if (instanceFiles.get(logFileName) == null) {
			instanceFile = new Logs(logFileName,clazz);
			instanceFiles.put(logFileName, instanceFile);
		} else {
			instanceFile = (Logs) instanceFiles.get(logFileName);
		}
		return instanceFile;
	}// end static synchronized public

	///////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *  生成的日志文件
	 */
	private Logs(String logFileName,@SuppressWarnings("rawtypes") Class clazz) {
		this.logFileName=logFileName;
		if(clazz!=null)
			this.className=clazz.getSimpleName()+" ";
		try {
			InputStream in =Logs.class.getResourceAsStream("/logs.properties");
			if (in != null) {
				prop.load(in);
			}
			this.logFilePath = prop.getProperty("logFilePath", System.getProperty("catalina.home")+File.separator+"logs");
			
			this.projectName = prop.getProperty("projectName");
			
			if(this.projectName==null){
				String url = Logs.class.getResource("/logs.properties").getPath();
				if(url.indexOf("WEB-INF")!=-1){
					url = new File(url).getPath();
					url = url.substring(0, url.indexOf(File.separator+"WEB-INF"+File.separator+"classes"));
					File ff=new File(url);
					this.projectName = "["+ff.getName()+"] ";
				}else{
					this.projectName="";
				}
			}else{
				this.projectName+=" ";
			}
			
			this.isConsoleOut = Integer.parseInt(prop.getProperty("isConsoleOut", "0"));
			this.isFileOut = Integer.parseInt(prop.getProperty("isFileOut", "0"));
			
			Logs.fileMaxSize = Long.parseLong(prop.getProperty("fileMaxSize", "10"));
			this.maxFileNumber = Integer.parseInt(prop.getProperty("maxFileNumber", "5"));
			this.timeout_alarm = Integer.parseInt(prop.getProperty("timeout_alarm", "60"));
			
			if(logFilePath.indexOf("{catalina.home}")!=-1){
				logFilePath=logFilePath.replace("{catalina.home}", System.getProperty("catalina.home",""));
			}
			
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}

	//////////////////////////////////////////////////////////////////////////////////////
	/**
	 * 输出一个空行
	 */
	public void newLine() {
		this.infoNewLine();
	}
	public void infoNewLine() {
		if (isFileOut == 1) {
			if(infoFileOutLog==null){
				this.newInfoFile();
			}
			infoFileOutLog.println("\n");
		} 
		
		if (isConsoleOut == 1) {
			System.out.println();
		}
	}
	public void warnNewLine() {
		if (isFileOut == 1) {
			if(warnFileOutLog==null){
				this.newWarnFile();
			}
			warnFileOutLog.println("\n");
		} 
		if (isConsoleOut == 1) {
			System.out.println();
		}
	}
	public void errorNewLine() {
		if (isFileOut == 1) {
			if(errorFileOutLog==null){
				this.newErrorFile();
			}
			errorFileOutLog.println("\n");
		} 
		if (isConsoleOut == 1) {
			System.out.println();
		}
	}
	//////////////////////////////////////////////////////////////////////////////////
	/**
	 * 信息日志
	 * @param info 信息
	 */
	public synchronized void info(String info) {
		endTime=System.currentTimeMillis();
		if(startTime==0)
			startTime=endTime;
		double consumingTime=(endTime-startTime);
		if (isFileOut == 1) {
			if(infoFileOutLog==null){
				this.newInfoFile();
			}
			infoFileOutLog.println(this.projectName+getDatetime()+this.formatConsumingTime(consumingTime)+" "+this.className+info);
		} 
		
		if (isConsoleOut == 1) {
			System.out.println(this.projectName+getDatetime()+this.formatConsumingTime(consumingTime)+" "+this.className+info);
		}
		
		if(consumingTime>(1000*this.timeout_alarm))//大于2秒生成告警
			this.timeover("耗时超过"+this.timeout_alarm+"秒，耗时："+this.formatConsumingTime(consumingTime)+"，"+info);
		startTime=endTime;
	}

	/**
	 * 信息日志
	 * @param info   信息
	 */
	public void info(String info,Throwable e) {
		endTime=System.currentTimeMillis();
		if(startTime==0)
			startTime=endTime;
		double consumingTime=(endTime-startTime);
		if (isFileOut == 1) {
			if(infoFileOutLog==null){
				this.newInfoFile();
			}
			infoFileOutLog.println(this.projectName+getDatetime()+this.formatConsumingTime(consumingTime)+" "+this.className+info);
			e.printStackTrace(infoFileOutLog);
		} 
		
		if (isConsoleOut == 1) {
			System.out.println(this.projectName+getDatetime()+this.formatConsumingTime(consumingTime)+" "+this.className+info);
			e.printStackTrace();
		}
		
		if(consumingTime>(1000*this.timeout_alarm))//大于2秒生成告警
			this.timeover("耗时超过"+this.timeout_alarm+"秒，耗时："+this.formatConsumingTime(consumingTime)+"，"+info);
		
		startTime=endTime;
	}
	//////////////////////////////////////////////////////////////////////////////////////////
	/**
	 * 信息日志
	 * @param info 信息
	 */
	public void warn(String info) {
		this.warn(info, true);
	}
	private void warn(String info,Boolean isWriteInfo) {
		if (isFileOut == 1) {
			if(warnFileOutLog==null){
				this.newWarnFile();
			}
			warnFileOutLog.println(this.projectName+getDatetime()+this.className+"警告："+info);
			if(isWriteInfo){
				if(infoFileOutLog==null){
					this.newInfoFile();
				}
				infoFileOutLog.println(this.projectName+getDatetime()+"              "+this.className+" 警告："+info);
			}
			
		} 
		
		if (isConsoleOut == 1) {
			System.out.println(this.projectName+getDatetime()+"          "+this.className+" 警告："+info);
		}
	}
	
	/**
	 * 信息日志
	 * @param info   信息
	 */
	public void warn(String info,Throwable e) {
		this.warn(info, true, e);
	}
	/**
	 * 信息日志
	 * @param info   信息
	 */
	public void warn(String info,Boolean isWriteInfo,Throwable e) {
		if (isFileOut == 1) {
			if(warnFileOutLog==null){
				this.newWarnFile();
			}
			warnFileOutLog.println(this.projectName+getDatetime()+this.className+"警告："+info);
			e.printStackTrace(warnFileOutLog);
			
			if(isWriteInfo){
				if(infoFileOutLog==null){
					this.newInfoFile();
				}
				infoFileOutLog.println(this.projectName+getDatetime()+"              "+this.className+" 警告："+info);
				e.printStackTrace(infoFileOutLog);
				
			}
		} 
		
		if (isConsoleOut == 1) {
			System.out.println(this.projectName+getDatetime()+"          "+this.className+" 警告："+info);
			e.printStackTrace();
		}
	}


	/**
	 * 错误日志
	 */
	public void error(String errorInfo) {
		if (isFileOut == 1) {
			if(infoFileOutLog==null){
				this.newInfoFile();
			}
			infoFileOutLog.println(this.projectName+getDatetime()+" "+this.className+"错误："+errorInfo);
			
			if(errorFileOutLog==null){
				this.newErrorFile();
			}
			errorFileOutLog.println(this.projectName+getDatetime()+" "+this.className+"错误："+errorInfo);
		} 
		
		if (isConsoleOut == 1) {
			System.err.println(this.projectName+getDatetime()+" "+this.className+"错误："+errorInfo);
		}

	}

	/**
	 * 错误日志
	 */
	public void error(String errorInfo, Throwable e) {
		if (isFileOut == 1) {
			if(infoFileOutLog==null){
				this.newInfoFile();
			}
			infoFileOutLog.println(this.projectName+getDatetime()+" "+this.className+"错误："+errorInfo);
			
			if(errorFileOutLog==null){
				this.newErrorFile();
			}
			errorFileOutLog.println(this.projectName+getDatetime()+" "+this.className+"错误："+errorInfo);
			
			e.printStackTrace(infoFileOutLog);
			e.printStackTrace(errorFileOutLog);

		} 
		if (isConsoleOut == 1) {
			System.err.println(this.projectName+getDatetime()+" "+this.className+"错误："+errorInfo);
			e.printStackTrace();
		}

	}
	
	
	/**
	 * 超时日志
	 */
	public void timeover(String timeoverInfo) {
		if (isFileOut == 1) {
			if(infoFileOutLog==null){
				this.newTimeoverFile();
			}
			infoFileOutLog.println(this.projectName+getDatetime()+" "+this.className+"超时："+timeoverInfo);
			
			if(timeoverFileOutLog==null){
				this.newTimeoverFile();
			}
			timeoverFileOutLog.println(this.projectName+getDatetime()+" "+this.className+"超时："+timeoverInfo);
			

		} 
		if (isConsoleOut == 1) {
			System.err.println(this.projectName+getDatetime()+" "+this.className+"超时："+timeoverInfo);
		}

	}

	////////////////////////////////////////////////////////////////////////////


	/**
	 * 清空文件内容
	 */
	public synchronized void clear() {
		try {
			File infoF = new File (this.infoFileFullName);
			FileWriter infoFW = new FileWriter (infoF);
			infoFW.write("");
			infoFW.flush();
			infoFW.close();
			
			File warnF = new File (this.warnFileFullName);
			FileWriter warnFW = new FileWriter (warnF);
			warnFW.write("");
			warnFW.flush();
			warnFW.close();
			
			File errorF = new File (this.errorFileFullName);
			FileWriter errorFW = new FileWriter (errorF);
			errorFW.write("");
			errorFW.flush();
			errorFW.close();
			
			File timeoverF = new File (this.timeoverFileFullName);
			FileWriter timeoverFW = new FileWriter (timeoverF);
			timeoverFW.write("");
			timeoverFW.flush();
			timeoverFW.close();
		} catch (FileNotFoundException ex) {
			System.out.println("can not clear logFile");
		} catch (IOException x) {
			System.out.println("can not clear logFile");
		}
	}
	
	/**
	 * 新文件 
	 */
	public synchronized void newInfoFile() {
		try {
			List<File> list=loadFile(logFileName+"\\."+".{10}\\.info(*)\\.log",logFilePath);
			if(list.size()==0){
				this.infoFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".info.log";
			}else if(list.size()<this.maxFileNumber){
				this.infoFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".info-"+list.size()+".log";
			}else{
				//1 找出最早的文件和最新的文件
				File minFile=list.get(0);	//最早文件
				Integer minFileNumber=9999;    //最小文件号
				Integer maxFileNumber=0;	//最大文件号
				
				for(File ff:list){
					//a 解析出文件编号
					String fileName=ff.getName();
					int beginIndex=fileName.indexOf("info-");
					int endIndex=fileName.lastIndexOf(".log");
					
					int fileNumber=0;
					
					if(beginIndex>-1){
						fileNumber=Integer.parseInt(fileName.substring(beginIndex+5,endIndex));
					}
					
					
					//b 分析出最小文件号和最大文件号
					if(fileNumber<=minFileNumber){
						minFileNumber=fileNumber;
						minFile=ff;
					}
					
					if(fileNumber>maxFileNumber){
						maxFileNumber=fileNumber;
					}
				}
				
				//2 删除最小文件
				minFile.delete();
				
				//3 生成新的文件
				this.infoFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".info-"+(maxFileNumber+1)+".log";
				
			}

			if(this.infoFileOutLog!=null){
				this.infoFileOutLog.close();
			}
			
			this.infoFileOutLog = new PrintWriter(new FileWriter(this.infoFileFullName, true), true);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public synchronized void newWarnFile() {
		try {
			List<File> list=loadFile(logFileName+"\\."+".{10}\\.warn(*)\\.log",logFilePath);
			if(list.size()==0){
				this.warnFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".warn.log";
			}else if(list.size()<this.maxFileNumber){
				this.warnFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".warn-"+list.size()+".log";
			}else{
				//1 找出最早的文件和最新的文件
				File minFile=list.get(0);	//最早文件
				Integer minFileNumber=9999;    //最小文件号
				Integer maxFileNumber=0;	//最大文件号
				
				for(File ff:list){
					//a 解析出文件编号
					String fileName=ff.getName();
					int beginIndex=fileName.indexOf("warn-");
					int endIndex=fileName.lastIndexOf(".log");
					
					int fileNumber=0;
					
					if(beginIndex>-1){
						fileNumber=Integer.parseInt(fileName.substring(beginIndex+5,endIndex));
					}
					
					
					//b 分析出最小文件号和最大文件号
					if(fileNumber<=minFileNumber){
						minFileNumber=fileNumber;
						minFile=ff;
					}
					
					if(fileNumber>maxFileNumber){
						maxFileNumber=fileNumber;
					}
				}
				
				//2 删除最小文件
				minFile.delete();
				
				//3 生成新的文件
				this.warnFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".warn-"+(maxFileNumber+1)+".log";
				
			}
			if(this.warnFileOutLog!=null){
				this.warnFileOutLog.close();
			}
			this.warnFileOutLog = new PrintWriter(new FileWriter(this.warnFileFullName, true), true);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public synchronized void newErrorFile() {
		try {
			List<File> list=loadFile(logFileName+"."+this.getDatetime2()+".error(*).log",logFilePath);
			if(list.size()==0){
				this.errorFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".error.log";
			}else if(list.size()<this.maxFileNumber){
				this.errorFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".error-"+list.size()+".log";
			}else{
				//1 找出最早的文件和最新的文件
				File minFile=list.get(0);	//最早文件
				Integer minFileNumber=9999;    //最小文件号
				Integer maxFileNumber=0;	//最大文件号
				
				for(File ff:list){
					//a 解析出文件编号
					String fileName=ff.getName();
					int beginIndex=fileName.indexOf("error-");
					int endIndex=fileName.lastIndexOf(".log");
					
					int fileNumber=0;
					
					if(beginIndex>-1){
						fileNumber=Integer.parseInt(fileName.substring(beginIndex+6,endIndex));
					}
					
					
					//b 分析出最小文件号和最大文件号
					if(fileNumber<=minFileNumber){
						minFileNumber=fileNumber;
						minFile=ff;
					}
					
					if(fileNumber>maxFileNumber){
						maxFileNumber=fileNumber;
					}
				}
				
				//2 删除最小文件
				minFile.delete();
				
				//3 生成新的文件
				this.errorFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".error-"+(maxFileNumber+1)+".log";
				
			}
			if(this.errorFileOutLog!=null){
				this.errorFileOutLog.close();
			}
			this.errorFileOutLog = new PrintWriter(new FileWriter(this.errorFileFullName, true), true);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void newTimeoverFile() {
		try {
			List<File> list=loadFile(logFileName+"."+this.getDatetime2()+".timeover(*).log",logFilePath);
			if(list.size()==0){
				this.timeoverFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".timeover.log";
			}else if(list.size()<this.maxFileNumber){
				this.timeoverFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".timeover-"+list.size()+".log";
			}else{
				//1 找出最早的文件和最新的文件
				File minFile=list.get(0);	//最早文件
				Integer minFileNumber=9999;    //最小文件号
				Integer maxFileNumber=0;	//最大文件号
				
				for(File ff:list){
					//a 解析出文件编号
					String fileName=ff.getName();
					int beginIndex=fileName.indexOf("timeover-");
					int endIndex=fileName.lastIndexOf(".log");
					
					int fileNumber=0;
					
					if(beginIndex>-1){
						fileNumber=Integer.parseInt(fileName.substring(beginIndex+6,endIndex));
					}
					
					
					//b 分析出最小文件号和最大文件号
					if(fileNumber<=minFileNumber){
						minFileNumber=fileNumber;
						minFile=ff;
					}
					
					if(fileNumber>maxFileNumber){
						maxFileNumber=fileNumber;
					}
				}
				
				//2 删除最小文件
				minFile.delete();
				
				//3 生成新的文件
				this.timeoverFileFullName=logFilePath+File.separator+logFileName+"."+this.getDatetime2()+".timeover-"+(maxFileNumber+1)+".log";
				
			}
			if(this.timeoverFileOutLog!=null){
				this.timeoverFileOutLog.close();
			}
			this.timeoverFileOutLog = new PrintWriter(new FileWriter(this.timeoverFileFullName, true), true);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//文件大小
	public long getInfoLength(){
		 File f=new File(this.infoFileFullName);
		 return f.length();
	}
	public long getWarnLength(){
		File f=new File(this.warnFileFullName);
		return f.length();
	}
	public long getErrorLength(){
		File f=new File(this.errorFileFullName);
		return f.length();
	}
	
	

	private String getDatetime() {
		// 时间
		Date rightNow = new Date();
		String dateString = (new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(rightNow);
		return dateString;
	}
	private String getDatetime2() {
		// 时间
		Date rightNow = new Date();
		String dateString = (new java.text.SimpleDateFormat("yyyy-MM-dd")).format(rightNow);
		return dateString;
	}
	
	//加载配置文件
	private List<File> loadFile(String pattenExpression,String dir){
		List<File> fileList=new ArrayList<File>();
		FileFilterUtil ffu=new FileFilterUtil(pattenExpression);
		ffu.listFilesByFilenameFilter(dir,fileList);
		return fileList;
	}
	
	//计算耗时
	private String formatConsumingTime(double consumingTime){
		if(consumingTime<1000){
			return " ("+formatString(consumingTime,3,0)+"ms)";
		}else if(consumingTime>=1000 && consumingTime<(60*1000) ){
			Double ss_s=consumingTime/1000;
			return " ("+formatString(ss_s,5,1)+"s)";
		}else{
			Double ss_m=consumingTime/1000/60;
			return " ("+formatString(ss_m,5,1)+"M)";
		}
	}
	
	private String formatString(double value,int num,int decimal){
		String str=String.format("%."+decimal+"f", value).toString();
		if(str.length()<num){
			for(int i=str.length();i<num;i++){
				str="0"+str;
			}
		}
		return str;
	}
}
